﻿
using System;
using System.ComponentModel;
using System.Windows.Forms;

namespace ExtendedDotNET.Controls.ImageCheckBox
{
    public partial class ImageCheckBox : UserControl
    {
        #region Fields.

        private bool _checked;
        private CheckState _checkstate = CheckState.Unchecked;
        private bool _threeState;
        private bool _autoCheck = true;

        #endregion

        #region Delegates.

        public delegate void CheckedChangeHandler(object sender, CheckedChangeEventArgs e);
        public delegate void CheckStateChangeHandler(object sender, CheckStateChangeEventArgs e);

        #endregion

        #region Constructors.

        public ImageCheckBox()
        {
            InitializeComponent();

            SetStyle(ControlStyles.Selectable, true);
            TabStop = true;

            SetCheckedImage();

            lblText.Text = Name;
        }

        #endregion

        #region Events And Implementation.

        [Category("Misc")]
        [Description("Occurs whenever the Check property is changed.")]
        public event CheckedChangeHandler CheckedChange;

        protected virtual void OnCheckedChange()
        {
            if (CheckedChange != null)
            {
                var args = new CheckedChangeEventArgs {Checked = _checked};
                CheckedChange(this, args);
            }
        }

        [Category("Misc")]
        [Description("Occurs whenever the Check State property is changed.")]
        public event CheckStateChangeHandler CheckStateChange;

        protected virtual void OnCheckStateChange()
        {
            if (CheckStateChange != null)
            {
                var args = new CheckStateChangeEventArgs { Checked = _checked, CheckState = _checkstate};
                CheckStateChange(this, args);
            }
        }

        #endregion

        #region Browsable Properties.

        [Browsable(true)]
        [Description("Indicates whether the component is in the checked state.")]
        [Category("Appearance")]
        [DefaultValue(false)]
        public bool Checked
        {
            get
            {
                return _checked;
            }
            set
            {
                _checked = value;
                SetCheckedImage();
                OnCheckedChange();
                Invalidate();
            }
        }

        [Browsable(true)]
        [Description("The text associated with the control.")]
        [Category("Appearance")]
        [DefaultValue("")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] 
        [Bindable(true)] 
        public override string Text
        {
            get
            {
                return lblText.Text;
            }
            set
            {
                lblText.Text = value;
                Invalidate();
            }
        }

        [Browsable(true)]
        [Description("Indicates the state of the component.")]
        [Category("Appearance")]
        [DefaultValue(CheckState.Unchecked)]
        public CheckState CheckState
        {
            get
            {
                return _checkstate;
            }
            set
            {
                _checkstate = value;
                SetCheckedFromState();
                OnCheckStateChange();
                Invalidate();
            }
        }

        [Browsable(true)]
        [Description("Indicates whether the ImageCheckBox will allow three check states rather than two.")]
        [Category("Behavior")]
        [DefaultValue(false)]
        public bool ThreeState
        {
            get
            {
                return _threeState;
            }
            set
            {
                _threeState = value;
                Invalidate();
            }
        }

        [Browsable(true)]
        [Description("Causes the ImageCheckBox to automatically change state when clicked.")]
        [Category("Behavior")]
        [DefaultValue(true)]
        public bool AutoCheck
        {
            get
            {
                return _autoCheck;
            }
            set
            {
                _autoCheck = value;
                Invalidate();
            }
        }

        #endregion

        #region Private Methods.

        private void SetCheckedFromState()
        {
            if(_checkstate == CheckState.Unchecked)
            {
                picCheck.Image = imageList.Images["Unchecked"];
                _checked = false;
            }
            else if(_checkstate == CheckState.Checked)
            {
                picCheck.Image = imageList.Images["Checked"];
                _checked = true;
            }
            else
            {
                picCheck.Image = imageList.Images["Indeterminated"];
                _checked = true;
            }
        }

        private void SetCheckedImage()
        {
            if(_checked)
            {
                picCheck.Image = imageList.Images["Checked"];
            }
            else
            {
                picCheck.Image = imageList.Images["Unchecked"];
                _checkstate = CheckState.Unchecked;
            }
        }

        private void SetChecked()
        {
            if (Checked)
            {
                Checked = false;
            }
            else
            {
                Checked = true;
            }
        }

        private void SetCheckedState()
        {
            if(CheckState == CheckState.Unchecked)
            {
                CheckState = CheckState.Checked;
            }
            else if (CheckState == CheckState.Checked)
            {
                CheckState = CheckState.Indeterminate;
            }
            else
            {
                CheckState = CheckState.Unchecked;
            }
        }

        #endregion

        #region Events.

        private void ImageCheckBox_Click(object sender, EventArgs e)
        {
            if (!_autoCheck)
                return;

            if (ThreeState)
            {
                SetCheckedState();
                OnCheckStateChange();
            }
            else
            {
                SetChecked();
                OnCheckedChange();
            }
        }

        private void picCheck_Click(object sender, EventArgs e)
        {
            if(!_autoCheck)
                return;
            
            if(ThreeState)
            {
                SetCheckedState();
                OnCheckStateChange();
            }
            else
            {
                SetChecked();
                OnCheckedChange();
            }
        }

        private void lblText_Click(object sender, EventArgs e)
        {
            if (!_autoCheck)
                return;

            if (ThreeState)
            {
                SetCheckedState();
                OnCheckStateChange();
            }
            else
            {
                SetChecked();
                OnCheckedChange();
            }
        }

        #endregion

        #region Tab Stop Events.

        protected override void OnMouseDown(MouseEventArgs e)
        {
            Focus();
            base.OnMouseDown(e);
        }

        private void lblText_MouseDown(object sender, MouseEventArgs e)
        {
            Focus();
            base.OnMouseDown(e);
        }

        private void picCheck_MouseDown(object sender, MouseEventArgs e)
        {
            Focus();
            base.OnMouseDown(e);
        }

        protected override bool IsInputKey(Keys keyData)
        {
            if (keyData == Keys.Up || keyData == Keys.Down) return true;
            if (keyData == Keys.Left || keyData == Keys.Right) return true;
            return base.IsInputKey(keyData);
        }

        protected override void OnEnter(EventArgs e)
        {
            Invalidate();
            base.OnEnter(e);
        }

        protected override void OnLeave(EventArgs e)
        {
            Invalidate();
            base.OnLeave(e);
        }

        protected override void OnPaint(PaintEventArgs pe)
        {
            base.OnPaint(pe);
            if (Focused)
            {
                var rc = ClientRectangle;
                rc.Inflate(-0, -0);
                ControlPaint.DrawFocusRectangle(pe.Graphics, rc);
            }
        }

        private void ImageCheckBox_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == (char)Keys.Space)
            {
                picCheck_Click(picCheck, new EventArgs());
            }
        }

        #endregion
    }

    #region Enums.

    public enum CheckState
    {
        Unchecked,
        Checked,
        Indeterminate
    }

    #endregion

    #region Classes.

    public class CheckedChangeEventArgs : EventArgs
    {
        public bool Checked { get; set; }
    }

    public class CheckStateChangeEventArgs : EventArgs
    {
        public bool Checked { get; set; }
        public CheckState CheckState { get; set; }
    }

    #endregion
}
